package com.redis.demo.common.utils;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * @Description: Redis工具类
 * @Author: lq
 * @Date: 2025/1/8 16:48
 */
@Slf4j
@Component
public class RedisUtils {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /** 看门狗任务-自动续约锁 */
    private ScheduledExecutorService watchdogTask;

    /** 锁的键 */
    private static final String LOCK_KEY = "lock_key";

    /** 锁的过期时间，单位：毫秒 */
    private static final long LOCK_TIMEOUT = 30000L;

    /** 锁的持有时间，单位：毫秒 */
    private static final long LOCK_EXPIRY_TIME = 5000L;

    /** 获取锁的Lua脚本 */
    private String getLockScript;

    /** 释放锁的Lua脚本 */
    private String releaseLockScript;

    /** 续约锁的Lua脚本 */
    private String renewLockScript;

    /**
     * 初始化时加载 Lua 脚本
     */
    @PostConstruct
    public void init() throws IOException {
        // 使用 ClassPathResource 加载 Lua 脚本
        getLockScript = loadScript("scripts/get_lock.lua");
        releaseLockScript = loadScript("scripts/release_lock.lua");
        renewLockScript = loadScript("scripts/renew_lock.lua");
    }

    /**
     * 加载 Lua 脚本
     *
     * @param scriptPath 脚本路径
     * @return
     * @throws IOException
     */
    private String loadScript(String scriptPath) throws IOException {
        // 读取 Lua 脚本
        ClassPathResource classPathResource = new ClassPathResource(scriptPath);
        // 读取文件内容
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(classPathResource.getInputStream()))) {
            StringBuilder script = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                script.append(line).append("\n");
            }
            return script.toString();
        }
    }

    /**
     * 获取分布式锁
     *
     * @param lockValue 锁的值，用来标识锁的拥有者，避免误删除锁
     * @return 获取锁是否成功
     */
    public boolean getLock(String lockValue) {
        try {
            List<String> keys = Collections.singletonList(LOCK_KEY);
            List<String> args = Arrays.asList(lockValue, String.valueOf(LOCK_EXPIRY_TIME));
            // 执行Lua脚本
            Long result = stringRedisTemplate.execute(
                    new DefaultRedisScript<>(getLockScript, Long.class),
                    keys,
                    args.toArray()
            );

            // 获取锁成功
            if (result != null && result == 1) {
                // ✅ 启动看门狗线程
                this.startWatchdog(lockValue);
                return true;
            }
            return false;
        } catch (Exception e) {
            log.error("获取锁失败", e);
            return false;
        }
    }

    /**
     * 释放分布式锁
     *
     * @param lockValue 锁的值，确保只有持有锁的线程可以释放锁
     */
    public void releaseLock(String lockValue) {
        try {
            List<String> keys = Collections.singletonList(LOCK_KEY);
            List<String> args = Collections.singletonList(lockValue);
            // 执行Lua脚本
            Long result = stringRedisTemplate.execute(
                    new DefaultRedisScript<>(releaseLockScript, Long.class),
                    keys,
                    args.toArray()
            );

            if (result != null && result == 1) {
                log.info("成功释放锁");
            }

            // ✅ 释放锁后，关闭看门狗
            if (watchdogTask != null) {
                watchdogTask.shutdown();
                watchdogTask = null;
            }

        } catch (Exception e) {
            log.error("释放锁失败", e);
        }
    }

    /**
     * 启动看门狗
     * @param lockValue
     */
    private void startWatchdog(String lockValue) {
        // 初始化定时任务（如果未启动）
        if (watchdogTask == null) {
            watchdogTask = Executors.newSingleThreadScheduledExecutor();
        }

        // 续期锁（避免锁提前释放）
        watchdogTask.scheduleAtFixedRate(() -> {
            try {
                List<String> keys = Collections.singletonList(LOCK_KEY);
                List<String> args = Arrays.asList(lockValue, String.valueOf(LOCK_EXPIRY_TIME));
                Long result = stringRedisTemplate.execute(
                        new DefaultRedisScript<>(renewLockScript, Long.class),
                        keys,
                        args.toArray()
                );
                if (result == null || result == 0) {
                    log.warn("看门狗停止：锁已失效");
                    // 终止看门狗
                    watchdogTask.shutdown();
                }
            } catch (Exception e) {
                log.error("看门狗续期失败", e);
            }
        }, LOCK_EXPIRY_TIME / 2, LOCK_EXPIRY_TIME / 2, TimeUnit.MILLISECONDS);
    }


}
